package com.bytz.modules.cms.statistic.util;

import com.bytz.common.exception.BytzBootException;
import com.bytz.modules.cms.statistic.enums.IntervalType;
import com.google.common.collect.Range;

import java.time.LocalDate;
import java.time.Month;
import java.time.Period;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.time.temporal.WeekFields;
import java.util.ArrayList;
import java.util.List;

public class DateRangeUtil {
    /**
     * 根据自定义时间范围枚举获取period对象
     */
    public static Period createPeriod(IntervalType intervalType) {
        switch (intervalType.getChronUnit()) {
            case DAYS:
                return Period.ofDays(intervalType.getTime());
            case WEEKS:
                return Period.ofWeeks(intervalType.getTime());
            case MONTHS:
                return Period.ofMonths(intervalType.getTime());
            case YEARS:
                return Period.ofYears(intervalType.getTime());
            default:
                throw new UnsupportedTemporalTypeException("Unsupported unit: " + intervalType.getChronUnit());
        }
    }

    /**
     * 根据日期和时间范围获取对应时间范围第一天
     * - eg
     * date: 2020-02-15 intervalType: MONTHS
     * return 2020-02-01
     * date: 2020-02-15 intervalType: YEAR
     * return 2020-01-01
     *
     * @param date         日期
     * @param intervalType 日期范围
     * @return 日期在对应日期范围的第一天
     */
    public static LocalDate getStartDateByInterval(LocalDate date, IntervalType intervalType) {
        switch (intervalType) {
            case YEARS:
                return date.with(TemporalAdjusters.firstDayOfYear());
            case HALF_YEARS:
                if (date.getMonth().getValue() > 6) {
                    return LocalDate.of(date.getYear(), 7, 1);
                } else {
                    return date.with(TemporalAdjusters.firstDayOfYear());
                }
            case QUARTERS:
                Month month = date.getMonth().firstMonthOfQuarter();
                return LocalDate.of(date.getYear(), month, 1);
            case MONTHS:
                return date.with(TemporalAdjusters.firstDayOfMonth());
            case WEEKS:
                return date.with(WeekFields.ISO.dayOfWeek(), 1);
            case DAYS:
                return date;
        }
        throw new BytzBootException("未知的时间范围");
    }

    /**
     * 根据日期和时间范围获取对应时间范围下一个周期的第一天
     * - eg
     * date: 2020-02-15 intervalType: MONTHS
     * return 2020-03-01
     * date: 2020-02-15 intervalType: YEAR
     * return 2021-01-01
     *
     * @param date         日期
     * @param intervalType 日期范围
     * @return 日期在对应日期范围的第一天
     */
    public static LocalDate getEndDateByInterval(LocalDate date, IntervalType intervalType) {
        switch (intervalType) {
            case YEARS:
                return date.with(TemporalAdjusters.firstDayOfNextYear());
            case HALF_YEARS:
                if (date.getMonth().getValue() > 6) {
                    return date.with(TemporalAdjusters.firstDayOfNextYear());
                } else {
                    return LocalDate.of(date.getYear(), 7, 1);
                }
            case QUARTERS:
                Month month = date.getMonth().firstMonthOfQuarter();
                return LocalDate.of(date.getYear(), month, 1).plus(Period.ofMonths(3));
            case MONTHS:
                return date.with(TemporalAdjusters.firstDayOfNextMonth());
            case WEEKS:
                return date.with(WeekFields.ISO.dayOfWeek(), 1).plus(Period.ofDays(7));
            case DAYS:
                return date.plus(Period.ofDays(1));
        }
        throw new BytzBootException("未知的时间范围");
    }

    /**
     * 根据日期范围和时间范围获取对应range的列表
     * eg:
     * start: 2020-02-15 end: 2020-07-15 intervalType: MONTHS
     * return: [[2022-02-01, 2022-03-01),
     *          [2022-03-01, 2022-04-01),
     *          [2022-04-01, 2022-05-01),
     *          [2022-05-01, 2022-06-01),
     *          [2022-06-01, 2022-07-01),
     *          [2022-07-01, 2022-08-01)]
     *
     * @param start    开始时间
     * @param end      结束时间
     * @param interval 时间范围
     * @return 开始时间到结束时间每个时间范围的列表
     */
    public static List<Range<LocalDate>> getRanges(LocalDate start, LocalDate end, IntervalType interval) {
        List<Range<LocalDate>> result = new ArrayList<>();
        LocalDate last = getStartDateByInterval(start, interval);
        while (true) {
            LocalDate rangeEnd = getEndDateByInterval(last, interval);
            result.add(Range.closedOpen(last, rangeEnd));
            last = rangeEnd;
            if (!end.isAfter(rangeEnd)) {
                return result;
            }
        }
    }
}
